package com.cyou.weplay.db;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.dbutils.ResultSetHandler;

/**
 * StringMapHandler
 *
 * @author zhaomingyu
 */
public class StringMapHandler implements ResultSetHandler<Map<String, String>> {

    @Override
    public Map<String, String> handle(ResultSet rs) throws SQLException {
        if (!rs.next()) {
            return null;
        }
        Map<String, String> result = new CaseInsensitiveHashMap();
        ResultSetMetaData rsmd = rs.getMetaData();
        int cols = rsmd.getColumnCount();

        for (int i = 1; i <= cols; i++) {
            Object o = rs.getObject(i);
            if (o == null) {
                result.put(rsmd.getColumnName(i), "");
            } else {
                result.put(rsmd.getColumnName(i), o.toString());
            }

        }

        return result;
    }

    private static class CaseInsensitiveHashMap extends HashMap<String, String> {

        /**
         * The internal mapping from lowercase keys to the real keys.
         *
         * <p>
         * Any query operation using the key ({@link #get(Object)}, {@link #containsKey(Object)}) is done in three steps:
         * <ul>
         * <li>convert the parameter key to lower case</li>
         * <li>get the actual key that corresponds to the lower case key</li>
         * <li>query the map with the actual key</li>
         * </ul>
         * </p>
         */
        private final Map<String, String> lowerCaseMap = new HashMap<String, String>();
        /**
         * Required for serialization support.
         *
         * @see java.io.Serializable
         */
        private static final long serialVersionUID = -2848100435296897392L;

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean containsKey(Object key) {
            Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));
            return super.containsKey(realKey);
            // Possible optimisation here:
            // Since the lowerCaseMap contains a mapping for all the keys,
            // we could just do this:
            // return lowerCaseMap.containsKey(key.toString().toLowerCase());
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String get(Object key) {
            Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));
            return super.get(realKey);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String put(String key, String value) {
            /*
             * In order to keep the map and lowerCaseMap synchronized,
             * we have to remove the old mapping before putting the
             * new one. Indeed, oldKey and key are not necessaliry equals.
             * (That's why we call super.remove(oldKey) and not just
             * super.put(key, value))
             */
            String oldKey = lowerCaseMap.put(key.toLowerCase(Locale.ENGLISH), key);
            String oldValue = super.remove(oldKey);
            super.put(key, value);
            return oldValue;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void putAll(Map<? extends String, ? extends String> m) {
            for (Map.Entry<? extends String, ? extends String> entry : m.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                this.put(key, value);
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String remove(Object key) {
            String realKey = lowerCaseMap.remove(key.toString().toLowerCase(Locale.ENGLISH));
            return super.remove(realKey);
        }
    }
}
